<template>
  <svg id="graph"></svg>

</template>

<script>
import {mapActions} from 'vuex'
import {deepCopy} from '../../../common/utils/object'
import {consoleGroup} from '../../../common/utils/message'
import config from '../utils/config'
import {getGridLayout, calcScale} from '../utils/layout'
import GraphEditorItem from "./GraphEditorItem";
import bus from "../utils/eventBus";

import GraphEditorGroup from "./GraphEditorGroup";


export default {


  components: {
    GraphEditorItem
  },

  name: 'GraphBoard',
  props: {
    data: {
      type: Object
    }
  },
  data() {
    return {
      origin: null,
      config: {...config},
      projectInfo: null,
      layouts: null,
      layoutMode: 'FORCE',
      s: [],

      //wx
      colorJudge: [],
      forNumber: 1,
      middleLinks: [],
      allNodes: [],
      hasDoubleClicked: [],

      links: [],
      delenodes: [],
      delelinks: [],
      focusNodes: [],
      selectedGroups: [],
      svgElements: {
        simulation: null,
        svg: null,
        defs: null,
        marker: null,
        view: null,
        root: null,
        focusGroup: null,

        //wx
        focusGroup_wx: null,

        svgLinks: null,
        svgLinksText: null,
        svgNodes: null,
        svgNodesText: null,
        svgMenuText: null,
        boundDrag: null,
        boundZoom: null,
        scale: null
      },
      flags: {
        loaded: false,
        singleFocus: true,
        enableFocus: true,
        pinned: false,
        locked: false,
        selectingMode: false
      }
    }
  },

  methods: {

    // ...mapActions(['updateLayout', 'saveAsPng', 'saveAsXml']),

    //wx
    ...mapActions({
      createItemAct: 'createItem',
      updateItemAct: 'updateItem',
      deleteItemNodeAct: 'deleteItemNode',
      showMore: 'showMore',
      updateLayout: 'updateLayout',
      saveAsPng: 'saveAsPng',
      saveAsXml: 'saveAsXml'
    }),
    /***** 重新挂载图谱 *****/
    mountGraphData(data, projectInfo) {
      //console.log('projectInfo:', projectInfo.projectId)
      this.origin = deepCopy(data)
      this.projectInfo = deepCopy(projectInfo)
      const {nodes, links, layouts} = data
      console.log("初始化数据", nodes, links, layouts)
      this.nodes = [...nodes]
      this.links = [...links]
      this.layouts = deepCopy(layouts)

      // console.log("布局验证：" + JSON.stringify(this.layouts))

      // consoleGroup('mountGraphData', () => {
      //   console.log('nodes:', this.nodes)
      //   console.log('links:', this.links)
      //   console.log('layouts:', this.layouts)
      // })

      this.init()
      this.flags.loaded = true
    },
    /***** 图谱绘制 *****/
    // 初始化图谱
    init() {
      const {
        // 全局变量 & 配置
        $d3,
        $el,
        config: {width, height},
        // 子例程
        reset,
        setSimulation,
        setView,
        setRoot,
        setFocusGroup,
        setLinks,
        setLinksText,
        setNodes,
        setNodesText,
        setMenuCircle,
        // 力导图绑定事件
        setDrag,
        setZoom,
        tick,
        // 图谱操作
        resetZoom,
        setEnableFocus,
        restoreLayout,

      } = this
      // 清除图谱
      reset()
      setEnableFocus(false)

      this.middleLinks = this.links

      const simulation = setSimulation()

      const svg = $d3
        .select($el)
        .attr('viewBox', [-width / 2, -height / 2, width, height])
      this.svgElements.svg = svg

      // 设置 defs 定义组
      // setDefs(svg)

      // 设置透明操作板
      const view = setView(svg)
      const root = setRoot(svg)

      // const g =  svg.append('g')
      //   .attr('transform', 'translate(' + marge.top + ',' + marge.left + ')')
      //   .attr('class', 'container')
      // this.linksText = g.append('g')
      //   .selectAll('text')
      //   .data(edges)
      //   .enter()
      //   .append('text')
      //   .attr('class', 'linksText')
      //   .text(function (d) {
      //     return d.relations
      //   })
      //   .style('font-size', 14)
      //   .attr('fill-opacity', 0)

      const boundZoom = setZoom(root)
      view.call(boundZoom)

      // 设置组
      setFocusGroup(root)

      // 设置关系、关系文字
      setLinks(root)
      setLinksText(root)


      const boundDrag = setDrag(simulation)
      const scale = $d3.scaleOrdinal($d3.schemeCategory10)
      this.svgElements.scale = scale
      this.$emit('init-property', 'nodeScale', scale)
      //设置图片
      // drawPattern(root)
      // 设置节点、节点文字
      setNodes(root, boundDrag, scale)
      setNodesText(root, boundDrag)

      //设置环形菜单
      setMenuCircle(root)

      simulation.on('tick', tick)

      // setTimeout(() => {
      //   resetZoom()
      // }, 300)
      setTimeout(() => {
        resetZoom()
      }, 600)
      setEnableFocus(true)
      restoreLayout()
    },
    // 重置图谱节点
    reset() {
      const {view, root} = this.svgElements
      if (view) view.remove()
      if (root) root.remove()
    },
    // 设置力导图初始化
    setSimulation() {
      const {
        $d3,
        nodes,
        links,
        config: {baseRadius}
      } = this
      const simulation = $d3
        .forceSimulation(nodes)
        .force(
          'link',
          $d3
            .forceLink(links)
            .id(d => d.id)
            .distance(
              d => (d.source.radius + d.target.radius) * 30 + baseRadius
            )
        )
        .force(
          'charge',
          $d3.forceManyBody().strength(d => -1000 - d.radius * 300)
        )
        .force('x', $d3.forceX())
        .force('y', $d3.forceY())
      this.svgElements.simulation = simulation
      return simulation
    },
    setDefs(svg) {
      const defs = this.svgElements.defs || svg.append('defs')
      this.svgElements.defs = defs
      return defs
    },
    // 设置拖曳背景图
    setView(svg) {
      const {width, height} = this.config
      const view = svg
        .append('rect')
        .attr('class', 'view')
        .style('fill', 'transparent')
        .attr('x', -width / 2)
        .attr('y', -height / 2)
        .attr('width', width)
        .attr('height', height)
        .on('click', this.clickView)
      this.svgElements.view = view
      return view
    },
    // 设置图谱根节点
    setRoot(svg) {
      const root = svg.append('g').attr('class', 'root')
      this.svgElements.root = root
      return root
    },


    // 设置图谱高亮组
    setFocusGroup(root) {
      const focusGroup = root
        .append('g')
        .attr('class', 'focus')
        .attr('stroke', 'skyblue')
        .attr('stroke-width', 20)
      this.svgElements.focusGroup = focusGroup
    },

    //设置环形菜单
    setMenuCircle(root) {
      const svgMenuText = root
        .append('g')
        .attr('class', 'Circle')
      this.svgElements.svgMenuText = svgMenuText
    },
    setLinks(root) {
      const {config: {baseRadius}, $d3,nodes,links, clickLink} = this
      console.log("root打印：" + JSON.stringify(root))

      var dis = 34 
      //修改箭头位置
      const arrowMarker = root
        .append('g')
        .append("defs")
        .append("marker")
        
        .attr("id", "arrow")
        .attr("viewBox", "0 -5 10 10")//坐标系的区域
        // .selectAll('circle')
        // .data(nodes)
        // .join('circle')
        // .attr("refX",function (d) {
        //   var res =d.radius + dis
        //   console.log("dddd"+res)
        //   return res ;
        // })
    
        // .attr("refX", baseRadius*2 - 5)//箭头坐标
        // value*3-47 value*4-
        .attr("refX", 65)//箭头坐标
        
        .attr("refY", 0)
        .attr("markerWidth", 6)//标识的大小
        .attr("markerHeight", 6)

        // .attr("markerUnits", "strokeWidth")
        // .attr("markerWidth", "5")
        // .attr("markerHeight", "5")
        // .attr("viewBox", "0 0 12 12")
        // .attr("refX", 69)
        // .attr("refY", "6")
        .attr("orient", "auto")
        

        
      const arrow_path = "M0,-5L10,0L0,5";
      // const arrow_path = "M2,2 L10,6 L2,10 L6,6 L2,2";
      arrowMarker.append("path")
        .attr("d", arrow_path)
        .attr("fill", "#999 ");


      // 设置关系线段
      const svgLinks = root
        .append('g')
        .attr('class', 'links')
        .attr('stroke', '#999')
        .attr('stroke-opacity', 1)
        .selectAll('line')
        .data(links)
        .join('line')
        .attr('stroke-width', d => d.value)
        .attr('id', d => `link-${d.id}`)
        .attr('data-id', d => d.id)
        // .attr('marker-end', d => `url(#arrow-${d.id})`)
        .attr("marker-end", "url(#arrow)")
        .attr("arrowValue", d => d.value)
        .on('click', clickLink)
      svgLinks.append('title').text(d => d.name)
      // arrowMarker.attr("refX", svgLinks.attr("a"));
      this.svgElements.svgLinks = svgLinks
      return svgLinks
    },
    setLinksText(root) {
      const {
        config: {font, fontSize},
        setLinksPositionRaio,
        links,
        clickLink
      } = this
      const svgLinksText = root
        .append('g')
        .attr('class', 'links_text')
        .selectAll('text')
        .data(links)
        .join('text')
        .style('fill', '#000000')
        .style('font', `${fontSize}px ${font}`)
        .style('user-select', 'none')
        .attr('dominant-baseline', 'middle')
        .attr('text-anchor', 'middle')
        .attr('data-id', d => d.id)
        .text(d => d.name)
        .on('click', clickLink)
      this.svgElements.svgLinksText = svgLinksText

      setLinksPositionRaio()

      return svgLinksText
    },

    // 绘制图片
    drawPattern() {
      console.log("执行了背景设置方法")
      const {
        config: {baseRadius},
        nodes,
        focus,
        unfocus,
        clickNode,
        root
      } = this

      let gPattern = root.append("g").attr("class", "g-pattern");

      //  添加pattern
      var pattern = gPattern.selectAll("pattern").data(nodes, function (d) {
        return "pattern" + d.id;
      });

      // 赋予宽高
      pattern
        .enter()
        .append("pattern")
        .attr("id", function (d) {
          return "pattern" + d.id;
        })
        .attr("x", 0)
        .attr("y", 0)
        .attr("height", function (d) {
          var imageHeight;
          // if (d.group === "态势") {
          if (d.group === "一级分类") {
            imageHeight = 78;
          } else if (d.group === "参与方") {
            imageHeight = 60;
          } else if (d.group === "作战单元") {
            imageHeight = 45;
          } else {
            imageHeight = 55;
          }
          return imageHeight
        })
        .attr("width", function (d) {
          var imageWidth;
          // if (d.group === "态势") {
          if (d.group === "一级分类") {
            imageWidth = 78;
          } else if (d.group === "参与方") {
            imageWidth = 60;
          } else if (d.group === "作战单元") {
            imageWidth = 45;
          } else {
            imageWidth = 55;
          }
          return imageWidth
        })
        .append("image")
        .attr("preserveAspectRatio", "none")
        .attr("xlink:href", function (d) {
          console.log("imagePath2555:" + d.group)
          var imagePath = "";
          // if (d.group === "态势") {
          if (d.group === "一级分类") {
            imagePath = require("@/assets/tsh.png");
          } else if (d.group === "参与方") {
            imagePath = require("@/assets/cyf.png");
          } else if (d.group === "作战单元") {
            imagePath = require("@/assets/jc.png");
          } else {
            imagePath = require("@/assets/airfw.png");
          }
          return imagePath
        })
        .attr("x", 0)
        .attr("y", 0)
        .attr("height", function (d) {
          return (baseRadius + d.radius * 10) * 2;
        })
        .attr("width", function (d) {
          return (baseRadius + d.radius * 10) * 2
        });
    },
    setNodes(root, boundDrag, scale) {
      const {
        config: {baseRadius},
        nodes,
        focus,
        unfocus,
        clickNode,
        doubleClick,
        drawPattern,
        rightClickNode
      } = this

      nodes.filter(node => {
        node.isDelete = false
        node.isNew = false
        // console.log("456测试wx：" + JSON.stringify(node))
      })

      const svgNodes = root
        .append('g')
        .attr('class', 'nodes')
        .attr('stroke', '#ffffff')
        .attr('stroke-width', 1.5)
        .selectAll('circle')
        .data(nodes)
        .join('circle')
        .attr('r', d => baseRadius + d.radius * 10)
        .attr('fill', d => (d.color ? d.color : scale(d.group)))
        //.style('fill',function(d){ return "url(#" + "pattern" + d.id + ")"; })
        .attr('data-id', d => d.id)
        .call(boundDrag)
        .on('click', clickNode)
        .on('contextmenu', rightClickNode)
        .on('mouseover', focus)
        .on('mouseout', unfocus)
        .on('dblclick', doubleClick)
      svgNodes.append('title').text(d => d.name)
      this.svgElements.svgNodes = svgNodes
      return svgNodes
    },
    setNodesText(root, boundDrag) {
      const {
        $d3,
        config: {font},
        nodes,
        clickNode,
        focus,
        unfocus,
        doubleClick,
        rightClickNode,
      } = this
      const svgNodesText = root
        .append('g')
        .attr('class', 'nodes_text')
        .selectAll('text')
        .data(nodes)
        .join('text')
        .style('fill', '#000000')
        .style('font', d => `${d.textSize}px ${font}`)
        .style('user-select', 'none')
        .attr('dominant-baseline', 'middle')
        .attr('text-anchor', 'middle')
        .attr('data-id', d => d.id)
        .text(d => d.name)
        .call(boundDrag)
        .on('click', clickNode)
        .on('contextmenu', rightClickNode)
        .on('dblclick', doubleClick)
        .on('mouseover', focus)
        .on('mouseout', unfocus)
      this.svgElements.svgNodesText = svgNodesText

      // console.log("493调试：" + JSON.stringify($d3.selectAll('root').size()))

      return svgNodesText
    },
    // 设置 focus 节点
    setFocus(node) {
      const {
        svgElements: {focusGroup: fg},
        flags: {enableFocus},
        setNodeHighLight,
        updateHighLightNodes
      } = this
      this.clearFocus()
      if (!enableFocus) return

      if (fg.select(`#focus-node-${node.id}`).empty()) {
        setNodeHighLight(node, true)
        updateHighLightNodes()
      }
    },
    updateHighLightNodes() {
      const {
        config: {baseRadius},
        nodes,
        svgElements: {focusGroup: fg}
      } = this

      const originFocus = fg.selectAll('circle')
      fg.selectAll('circle')
        .data(nodes.filter(node => node.highLight || node.focus))
        .join('circle')
        .attr('r', d => baseRadius + d.radius * 10 + 5)
        .attr('fill', 'none')
        .attr('id', d => `focus-node-${d.id}`)
        .attr('cx', d => d.x)
        .attr('cy', d => d.y)
        .merge(originFocus)
    },
    // 力导图更新
    tick() {
      const {
        focusGroup,
        svgLinks,
        svgLinksText,
        svgNodes,
        svgNodesText,
        svgMenuText
      } = this.svgElements

      svgLinks
        .attr('x1', d => d.source.x)
        .attr('y1', d => d.source.y)
        .attr('x2', d => d.target.x)
        .attr('y2', d => d.target.y)

      svgLinksText
        .attr('x', d => {
          const {
            source: {x: x1},
            target: {x: x2},
            positionRatio
          } = d
          return x1 + (x2 - x1) * positionRatio
        })
        .attr('y', d => {
          const {
            source: {y: y1},
            target: {y: y2},
            positionRatio
          } = d
          return y1 + (y2 - y1) * positionRatio
        })

      svgNodes.attr('cx', d => d.x).attr('cy', d => d.y)
      svgNodesText.attr('x', d => d.x).attr('y', d => d.y)

      focusGroup
        .selectAll('circle')
        .attr('cx', d => d.x)
        .attr('cy', d => d.y)

      svgMenuText
        .select('menuCircle')
        .attr("transform", function (d) {
          var x1 = d.x
          var y1 = d.y
          return 'translate(' + x1 + ',' + y1 + ')';
        })
    },


    updateNodesWithText() {
      if (!this.flags.loaded) return
      const {
        $d3,
        config: {baseRadius, font, opacity},
        nodes,
        links,
        doubleClick,
        svgElements: {
          simulation,
          root,
          svgNodes,
          svgNodesText,
          boundDrag,
          scale
        },
        clickNode,
        focus,
        unfocus,
        updateAllOpacity,
        drawPattern,
        rightClickNode
      } = this
      // console.log('updateNodesWithText', nodes)
      updateAllOpacity()

      // console.log("节点更新测试222："+JSON.stringify(nodes))

      // update nodes
      //修改节点背景
      let _svgNodes = root
        .select('g.nodes')
        .selectAll('circle')
        .data(nodes)
        .join('circle')
        .attr('r', d => baseRadius + d.radius * 10)
        .attr('fill', d => (d.color ? d.color : scale(d.group)))
        // .attr('fill',"red")
        //.style('fill',function(d){ return "url(#" + "pattern" + d.id + ")"; })
        .attr('opacity', d => (d.opacity ? opacity : 1))
        // .attr('opacity', d => (d.opacity))
        .attr('data-id', d => d.id)
        .attr('cx', d => d.x)
        .attr('cy', d => d.y)
        .call(boundDrag)
        .on('click', clickNode)
        .on('contextmenu', rightClickNode)
        .on('dblclick', doubleClick)
        .on('mouseover', focus)
        .on('mouseout', unfocus)

      _svgNodes.append('title').text(d => d.name)
      _svgNodes = _svgNodes.merge(svgNodes)
      this.svgElements.svgNodes = _svgNodes

      // update nodes text
      const _svgNodesText = root
        .select('g.nodes_text')
        .selectAll('text')
        .data(nodes)
        .join('text')
        .style('fill', d => (d.isDelete ? '#ffffff33' : '#000000'))
        .style('font', d => `${d.textSize}px ${font}`)
        .style('user-select', 'none')
        .attr('dominant-baseline', 'middle')
        .attr('text-anchor', 'middle')
        .attr('data-id', d => d.id)
        .text(d => d.name)
        .call(boundDrag)
        .on('click', clickNode)
        .on('contextmenu', rightClickNode)
        .on('dblclick', doubleClick)
        .on('mouseover', focus)
        .on('mouseout', unfocus)
        .merge(svgNodesText)
      this.svgElements.svgNodesText = _svgNodesText

      // console.log("652测试：" + JSON.stringify(_svgNodesText.selectAll('text')))
      // console.log("nodes处理结果qfvsd："+JSON.stringify(nodes))

      bus.$emit("updataSearchNodes", nodes)
      // console.log("搜索传输节点（发）："+JSON.stringify(nodes))


      simulation.nodes(nodes)
      simulation.force('link').links(links)
      simulation.alpha(1).restart()
    },
    updateLinksWithText() {
      if (!this.flags.loaded) return
      const {
        config: {fontSize, font, opacity},
        nodes,
        links,
        svgElements: {simulation, root, svgLinks, svgLinksText},
        setLinksPositionRaio,
        clickLink,
        drawPattern,
        updateAllOpacity
      } = this
      // console.log('updateLinksWithText', links)
      updateAllOpacity()

      // console.log("关系初始化25225："+JSON.stringify(links))

      // update links
      let _svgLinks = root
        .select('g.links')// 选取元素
        .selectAll('line')
        .data(links)
        .join('line')
        .attr('stroke-width', d => d.value*2)//3)
        // .attr('opacity', d => (d.opacity ? opacity : 1))
        .attr('opacity', d => (d.opacity ? opacity : 1))
        .attr('id', d => `link-${d.id}`)
        .attr('data-id', d => d.id)
        // .attr('marker-end', d => `url(#arrow-${d.id})`)
        .attr('marker-end', d => `url(#arrow)`)
        .on('click', clickLink)
        .on('mouseover', function(d, i) {
          _svgLinks.style('stroke-width',  d => d.value*4)
            .style('stroke', '#000')
        })
        .on('mouseout', function(d, i) {
        _svgLinks
          .style('stroke-width', d => d.value*2)
          .style('stroke','#999')
          .attr('marker-mid', d => `url(#arrow)`)
        })
      _svgLinks.append('title').text(d => d.name)
      _svgLinks = _svgLinks.merge(svgLinks)
      this.svgElements.svgLinks = _svgLinks

      // update links text
      const _svgLinksText = root
        .select('g.links_text')
        .selectAll('text')
        .data(links)
        .join('text')
        .style('fill', '#000000')
        .style('font', `${fontSize}px ${font}`)
        // .attr('opacity', d => (d.opacity ? opacity : 1))
        .attr('opacity', d => (d.opacity ? opacity : 1))
        .style('user-select', 'none')
        .attr('dominant-baseline', 'middle')
        .attr('text-anchor', 'middle')
        .attr('data-id', d => d.id)
        // .text(d => d.name)
        .on('click', clickLink)
        .merge(svgLinksText)
      this.svgElements.svgLinksText = _svgLinksText
      simulation.nodes(nodes)
      simulation.force('link').links(links)
      simulation.alpha(1).restart()
      setLinksPositionRaio()
    },
    setLinksPositionRaio() {
      const {
        config: {baseRadius},
        links
      } = this
      links.forEach(link => {
        const {
          source: {radius: r1},
          target: {radius: r2}
        } = link
        link.positionRatio = (baseRadius + r1 * 20) / ((r1 + r2) * 30 + baseRadius)
      })
    },
    updateFocus() {
      const {
        config: {baseRadius},
        svgElements: {focusGroup: fg}
      } = this
      const originFocus = fg.selectAll('circle')
      fg.selectAll('circle')
        .data(this.nodes.filter(node => node.highLight || node.focus))
        .join('circle')
        .attr('r', d => baseRadius + d.radius * 10 + 5)
        .attr('fill', 'none')
        .attr('id', d => `focus-node-${d.id}`)
        .attr('cx', d => d.x)
        .attr('cy', d => d.y)
        .merge(originFocus)
    },
    /********** 外部节点操作 **********/
    createNode(node) {
      // console.log('[GraphBoard] createNode', node)
      const {
        nodes,
        updateNodesWithText,
        setNodeFocus,
        setFocus,
        layoutMode,
        restoreLayout
      } = this
      nodes.push(node)
      updateNodesWithText()
      setNodeFocus(node)
      setFocus(node)
      layoutMode === 'GRID' && restoreLayout(true)
    },
    createLink(link) {

      // console.log('[GraphBoard] createLink', link)
      const {links, updateLinksWithText} = this
      links.push(link)
      updateLinksWithText()
    },
    updateNode(node) {
      // console.log('[GraphBoard]', node)
      const {
        nodes,
        updateNodesWithText,
        setNodeFocus,
        updateFocus,
        layoutMode,
        restoreLayout
      } = this
      const _node = nodes.filter(_node => _node.id === node.id)[0]
      for (const prop in _node) {
        _node[prop] = node[prop]
      }
      updateNodesWithText()
      setNodeFocus(_node)
      updateFocus()
      layoutMode === 'GRID' && restoreLayout(true)
    },
    updateLink(link) {
      // console.log('[GraphBoard]', link)
      const _link = this.links.filter(_link => _link.id === link.id)[0]
      for (const prop in _link) {
        link[prop] = link[prop]
      }
      this.updateLinksWithText()
    },
    deleteNode(nodeId) {
      // console.log('[GraphBoard]', nodeId)
      const {
        nodes,
        links,
        updateNodesWithText,
        updateLinksWithText,
        clearFocus,
        layoutMode,
        restoreLayout
      } = this
      // delete node
      const node = nodes.filter(node => node.id === nodeId)[0]
      nodes.splice(nodes.indexOf(node), 1)

      // delenodes
      const _link = links.filter(_link => _link.to === node.id || _link.from === node.id)
      for (const prop in _link) {
        this.delelinks.push(_link[prop])
      }
      // delete links
      this.links = links.filter(
        ({from, to}) => from !== node.id && to !== node.id
      )
      updateNodesWithText()
      updateLinksWithText()
      clearFocus()
      layoutMode === 'GRID' && restoreLayout(true)
    },
    deleteLink(linkId) {
      // console.log('[GraphBoard]', linkId)
      const {links} = this
      const link = links.filter(link => link.id === linkId)[0]
      links.splice(links.indexOf(link), 1)
      this.updateLinksWithText()
    },
    updateData() {
      this.updateNodesWithText()
      this.updateLinksWithText()
    },
    /********** 外部图谱操作 **********/
    // 重置缩放
    resetZoom() {
      const {
        $d3,
        config,
        nodes,
        svgElements: {view, boundZoom}
      } = this
      const scale = calcScale(nodes, config)
      view
        .transition()
        .duration(750)
        .call(boundZoom.transform, $d3.zoomIdentity.scale(scale))
    },
    // 随机分布
    randomDisorder() {
      const {
        svgElements: {simulation}
      } = this
      if (this.layoutMode === 'FORCE') {
        this.nodes.forEach(node => {
          node.vx = node.vy = null
          node.x = node.y = null
          node.fx = node.fy = null
        })
        simulation.alphaTarget(0.3).restart()
      }
    },
    // 切换布局
    switchLayout(mode) {
      if (this.layoutMode !== mode) {
        this.layoutMode = mode
        this.restoreLayout()
      }
    },
    // 保存布局
    saveLayout() {
      const {layoutMode, nodes, layouts, updateLayout, projectInfo} = this
      const layoutNodes = nodes.map(({id, x, y}) => ({id, x, y}))
      // console.log(`saveLayout layout mode: ${layoutMode}`, layoutNodes)
      updateLayout({
        type: layoutMode,
        projectId: projectInfo.projectId,
        nodes: layoutNodes
      }).then(res => {
        if (res) {
          layouts[layoutMode].nodes = layoutNodes
        }
      })
    },
    // 恢复布局
    restoreLayout(bool) {
      const {
        config,
        layoutMode,
        layouts,
        nodes,
        pin,
        unPin,
        flags: {pinned},
        setLocked
      } = this
      if (
        bool ||
        (layoutMode === 'GRID' && layouts.GRID.nodes.length !== nodes.length)
      ) {
        layouts.GRID.nodes = getGridLayout(nodes, config)
      }
      const layoutNodesMapper = {}
      console.log('layouts:', layouts)
      console.log('layoutMode:', layoutMode)
      layouts[layoutMode].nodes.forEach(({id, x, y}) => {
        layoutNodesMapper[id] = {x, y}
      })
      console.log(`restoreLayout ${layoutMode}:`, layoutNodesMapper)

      unPin()
      nodes.forEach(node => {
        if (Reflect.has(layoutNodesMapper, node.id)) {
          const {x, y} = layoutNodesMapper[node.id]
          node.x = x
          node.y = y
          if (pinned) {
            node.fx = x
            node.fy = y
          }
        }
      })
      setLocked(layoutMode === 'GRID')
      layoutMode === 'FORCE' ? unPin() : pin()
    },
    // 选取类别
    selectGroups(groups) {
      // console.log('selectGroups', groups)
      this.setSelectedGroups(groups)
      this.updateNodesWithText()
      this.updateLinksWithText()
    },
    // 直接选中单个节点
    selectNode(nodeId) {
      // console.log('graph selectNode', nodeId)
      const node = this.getNodeById(nodeId)
      this.setFocus(node)
      this.setNodeFocus(node)
    },
    // 多个节点一次高亮
    highLightMultiple(nodeIds) {
      this.clearFocus()
      // console.log('highLightMultiple', nodeIds)
      const nodes = this.getNodesByIds(nodeIds)
      nodes.forEach(node => this.setNodeHighLight(node, true))
      this.updateHighLightNodes()
    },
    // 切换选中模式
    setSelectingMode(mode) {
      this.flags.selectingMode = mode
    },
    selectInSelectingMode() {
    },
    /********** 图谱操作 **********/
    // 设置高亮组
    setFocusNodes(nodeIds) {
      const {
        config: {baseRadius},
        focusNodes,
        svgElements: {focusGroup: fg},
        getNodesByIds,
        setNodeFocus
      } = this
      const targetNodes = getNodesByIds(nodeIds)
      targetNodes.forEach(node => setNodeFocus(node))

      fg.selectAll('circle')
        .data(focusNodes)
        .enter()
        .insert('circle')
        .attr('r', d => baseRadius + d.radius * 10 + 5)
        .attr('fill', 'none')
        .attr('id', d => `focus-node-${d.id}`)
        .attr('cx', d => d.x)
        .attr('cy', d => d.y)
    },
    // 清除高亮
    clearFocus() {
      this.svgElements.focusGroup.selectAll('circle').remove()
      this.svgElements.root.select('.menuCircle').remove()
      this.nodes.forEach(node => {
        this.setNodeHighLight(node, false)
        this.clearNodeFocus(node)
      })
    },
    // 固定节点
    pin() {
      const {
        nodes,
        flags: {pinned}
      } = this
      if (!pinned) {
        this.flags.pinned = true
        nodes.forEach(node => {
          node.fx = node.x
          node.fy = node.y
        })
      }
    },
    // 取消固定
    unPin() {
      const {
        svgElements: {simulation},
        nodes
      } = this
      this.flags.pinned = false
      nodes.forEach(node => {
        node.fx = null
        node.fy = null
      })
      simulation.alpha(1).restart()
    },
    /********** 节点/关系操作事件 **********/
    //设置节点环形菜单
    // toggleCircle(current, d) {
    //   const {
    //     $d3,
    //     config: {baseRadius},
    //     svgElements: {boundDrag},
    //     boundDragsingle,
    //     deleteNode,
    //     addMyLinks,
    //     deleteMyLinks
    //   } = this
    //
    //   var currentD = d
    //   const id = Number(currentD.id)
    //   if (id > 0) {
    //     current.select('.menuCircle').remove()
    //   }
    //   var data = [{
    //     population: 50,
    //     value: '收起',
    //     color: '#A4BF45',
    //     type: 'showOff'
    //   }, {
    //     population: 50,
    //     value: '',
    //     color: 'red',
    //     type: 'showOn'
    //   }]
    //   var sum = $d3.sum(data.map(function (d) {
    //     return d.population
    //   }))
    //   for (const i in data) {
    //     data[i].Percentage = (data[i].population / sum * 100).toFixed(0) + "%";
    //   }
    //   /*
    //   var drag= $d3.drag()
    //     //.origin(function(d) { return d; })
    //     .on("drag",function(d){
    //       d.dx+=$d3.event.dx
    //       d.dy+=$d3.event.dy
    //       $d3.select(this)
    //       .attr("transform","translate("+d.dx+","+d.dy+")")
    //     }) */
    //
    //   var width = baseRadius + currentD.radius * 80,
    //     height = baseRadius + currentD.radius * 80
    //
    //   var gw = current.select('.singleCircle')
    //     .attr("width", width)
    //     .attr("height", height)
    //
    //   var Pie = gw.append("g")
    //     .attr('class', 'menuCircle')
    //     .attr("transform", function (d) {
    //       var x1 = currentD.x
    //       var y1 = currentD.y
    //       return 'translate(' + x1 + ',' + y1 + ')';
    //     })
    //   //  .call(drag)
    //   var arc_generator = $d3.arc()
    //     .innerRadius(width / 6.5)
    //     .outerRadius(width / 4)
    //   var angle_data = $d3.pie()
    //     .value(function (d) {
    //       return d.population;
    //     })
    //   var pieData = angle_data(data)
    //   var pieAngle = pieData.map(function (p) {
    //     return (p.startAngle + p.endAngle) / 2 / Math.PI * 180;
    //   });
    //
    //   //wx
    //   console.log("wx测试执行了收缩方法2")
    //
    //   //生成内部圆环
    //   //var color=$d3.scaleOrdinal($d3.schemeCategory10)
    //   Pie.selectAll("path")
    //     .data(angle_data(data))
    //     .enter()
    //     .append("path")
    //     .attr("d", arc_generator)
    //     // .style("fill", function (d, i) {
    //     //  return color(i);
    //     // })
    //     .style("fill", function (d) {
    //       return d.data.color
    //     })
    //     .style('stroke', 'black')
    //     .attr("class", "path")
    //     .attr('type', function (d) {
    //       return d.data.type
    //     })
    //     .on('click', function (d) {
    //
    //       //wx测试
    //       console.log("wx测试3" + d.target.attributes['type'].value)
    //
    //       if (d.target.attributes['type'].value === 'showOn') {
    //         addMyLinks(currentD)
    //       } else {
    //         deleteMyLinks(currentD)
    //       }
    //       //this.$d3.event.stopPropagation()
    //     })
    //
    //   var arc_label = $d3.arc()
    //     .innerRadius(width / 6.5)
    //     .outerRadius(width / 4)
    //   Pie.selectAll(".arc_label")
    //     .data(angle_data(data))
    //     .enter()
    //     .append("path")
    //     .attr("d", arc_label)
    //     .attr("class", "arc_label")
    //     .style("fill", "none")
    //   const labelFontSize = 40;
    //   const labelValRadius = (currentD.radius * 50 * 0.35 - labelFontSize * 0.35); // 计算正确半径 文字位置
    //   const labelValRadius1 = (currentD.radius * 50 * 0.35 + labelFontSize * 0.35);
    //   // const labelsVals = current.select('.singleCircle').append('g')
    //   //  .classed('labelsvals', true);
    //   // 定义两条路径以使标签的方向正确
    //   Pie.append('def')
    //     .append('path')
    //     .attr('id', 'label-path-1')
    //     .attr('d', `m0 ${-labelValRadius} a${labelValRadius} ${labelValRadius} 0 1,1 -0.01 0`);
    //   Pie.append('def')
    //     .append('path')
    //     .attr('id', 'label-path-2')
    //     .attr('d', `m0 ${-labelValRadius1} a${labelValRadius1} ${labelValRadius1} 0 1,0 0.01 0`);
    //   Pie.selectAll('text')
    //     .data(angle_data(data))
    //     .enter()
    //     .append('text')
    //     .style('font-size', labelFontSize)
    //     .style('fill', 'black')
    //     .style('font-weight', "bold")
    //     .style('text-anchor', 'middle')
    //     .append('textPath')
    //     .attr('type', function (d) {
    //       return d.data.type
    //     })
    //     .attr('href', function (d, i) {
    //       const p = pieData[i];
    //       const angle = pieAngle[i];
    //       if (angle > 90 && angle <= 270) { // 根据角度选择路径
    //         return '#label-path-2';
    //       } else {
    //         return '#label-path-1';
    //       }
    //     })
    //     .attr('startOffset', function (d, i) {
    //       const p = pieData[i];
    //       const angle = pieAngle[i];
    //       let percent = (p.startAngle + p.endAngle) / 2 / 2 / Math.PI * 100;
    //       if (angle > 90 && angle <= 270) { // 分别计算每条路径的正确百分比
    //         return 100 - percent + "%";
    //       }
    //       return percent + "%";
    //     })
    //     .text(function (d) {
    //       return d.data.value;
    //     })
    //     .on('click', function (d) {
    //       if (d.target.attributes['type'].value === 'showOn') {
    //         addMyLinks(currentD)
    //       } else {
    //         deleteMyLinks(currentD)
    //       }
    //     }, true)
    //   this.svgElements.svgMenuText = Pie
    //   //console.log(`menu:, `, this.svgElements.svgMenuText)
    // },
    dragMove(d) {
      d.dx += this.$d3.event.dx
      d.dy += this.$d3.event.dy
      this.$d3.select(this)
        .attr("transform", "translate(" + d.dx + "," + d.dy + ")")
    },
    //
    // addMyLinks(nodeOrId) {
    //   console.log('[GraphBoard]', nodeOrId)
    //   const {
    //     $d3,
    //     nodes,
    //     links,
    //     updateNodesWithText,
    //     updateLinksWithText,
    //     createNode,
    //     createLink,
    //     clearFocus,
    //     layoutMode,
    //     restoreLayout,
    //     getNodeById,
    //
    //     setSimulation,
    //   } = this
    //   const nodeId = nodeOrId.id
    //   let isInclude = false
    //
    //   const data = {
    //     "entityId": nodeId,
    //     "entityName": "",
    //     "fuzzyQuery": true,
    //     "projectId": 0
    //   }
    //
    //   console.log("属性测试彩信相册VS的：" + nodeId)
    //
    //   if (this.hasDoubleClicked.includes(nodeId)) {
    //     console.log("请求测试：未向后端请求" + this.hasDoubleClicked)
    //     if (this.delelinks.length !== 0) {
    //       const _link = this.delelinks.filter(_link => _link.to === nodeId)
    //
    //       if (_link.length !== 0) {
    //         for (const prop in _link) {
    //           createLink(_link[prop])
    //           this.delelinks.splice(this.delelinks.indexOf(_link[prop]), 1)
    //         }
    //       }
    //       this.addLoopNode(nodeId)
    //       //console.log('[delelinks]',this.delelinks)
    //     }
    //
    //     updateNodesWithText()
    //     updateLinksWithText()
    //     clearFocus()
    //     this.svgElements.root.select('.menuCircle').remove()
    //     layoutMode === 'GRID' && restoreLayout(true)
    //
    //   } else {
    //     // console.log("请求测试：向后端请求了9841：" + nodeId)
    //     this.hasDoubleClicked.push(nodeId)
    //     this.showMore(data).then(res => {
    //       if (res) {
    //         console.log("收到双击数据了！！！！")
    //         res.nodes.forEach(node => {
    //           if (!this.allNodes.includes(node.id) || getNodeById(node.id).isNew) {
    //             res.links.forEach(link => {
    //               // console.log("请求测试：向后端请求了9841："  + JSON.stringify(link))
    //               if (link.target === node.id) {
    //                 this.middleLinks.push(link)
    //                 console.log("链接测试9846415112link"+JSON.stringify(link) )
    //               }
    //             })
    //
    //             console.log("1271双击测试sadasd："+node.id)
    //
    //             if (getNodeById(node.id)) {
    //               getNodeById(node.id).isNew = false
    //             } else {
    //               node.isNew = false
    //               node.isDelete = false
    //               node.opacity = false
    //               nodes.push(node)
    //               if (!this.selectedGroups.includes(node.group)) {
    //                 this.selectedGroups.push(node.group)
    //               }
    //               this.allNodes.push(node.id)
    //             }
    //           }
    //         })
    //
    //         bus.$emit("changeAllGroups", this.selectedGroups)
    //
    //         // this.$emit('recieveGroup',this.selectedGroups)
    //
    //         // this.links = this.middleLinks
    //
    //         if (this.delelinks.length !== 0) {
    //           const _link = this.delelinks.filter(_link => _link.to === nodeId)
    //
    //           if (_link.length !== 0) {
    //             for (const prop in _link) {
    //               createLink(_link[prop])
    //               this.delelinks.splice(this.delelinks.indexOf(_link[prop]), 1)
    //             }
    //           }
    //           this.addLoopNode(nodeId)
    //         }
    //
    //         updateNodesWithText()
    //         updateLinksWithText()
    //         clearFocus()
    //         this.svgElements.root.select('.menuCircle').remove()
    //         layoutMode === 'GRID' && restoreLayout(true)
    //       }
    //     })
    //   }
    // },
    addMyLinks(nodeOrId) {
      console.log('[GraphBoard]', nodeOrId)
      const {
        $d3,
        nodes,
        links,
        updateNodesWithText,
        updateLinksWithText,
        createNode,
        createLink,
        clearFocus,
        layoutMode,
        restoreLayout,
        getNodeById,
        isContains,

        setSimulation,
      } = this
      const nodeId = nodeOrId.id
      let isInclude = false
      const projectId = Number(this.$route.params.projectId)
      const data = {
        "entityId": nodeId,
        "entityName": "",
        "fuzzyQuery": true,
        "projectId": projectId
      }

      // console.log("属性测试彩信相册VS的：" + nodeId)

      if (this.hasDoubleClicked.includes(nodeId)) {
        console.log("请求测试：未向后端请求" + this.hasDoubleClicked)
        if (this.delelinks.length !== 0) {
          const _link = this.delelinks.filter(_link => _link.to === nodeId)

          if (_link.length !== 0) {
            for (const prop in _link) {
              createLink(_link[prop])
              this.delelinks.splice(this.delelinks.indexOf(_link[prop]), 1)
            }
          }
          this.addLoopNode(nodeId)
          //console.log('[delelinks]',this.delelinks)
        }

        updateNodesWithText()
        updateLinksWithText()
        clearFocus()
        this.svgElements.root.select('.menuCircle').remove()
        layoutMode === 'GRID' && restoreLayout(true)

      } else {
        console.log("请求测试：向后端请求了9841：" + nodeId)
        this.hasDoubleClicked.push(nodeId)
        this.showMore(data).then(res => {
          if (res) {
            console.log("收到双击数据了！！！！")
            res.nodes.forEach(node => {
              res.links.forEach(link => {
                // console.log("请求测试：向后端请求了9841："  + JSON.stringify(link))
                if (link.target === node.id) {
                  this.middleLinks.push(link)
                  console.log("链接测试9846415112link", link)
                }
              })

              console.log("1271双击测试sadasd：" + node.id)

              if (getNodeById(node.id)) {
                getNodeById(node.id).isNew = false
                console.log("1427")
              } else {
                node.isNew = false
                node.isDelete = false
                node.opacity = false
                nodes.push(node)
                if (!this.selectedGroups.includes(node.group)) {
                  this.selectedGroups.push(node.group)
                }
                this.allNodes.push(node.id)
              }

            })

            bus.$emit("changeAllGroups", this.selectedGroups)

            // this.$emit('recieveGroup',this.selectedGroups)

            // this.links = this.middleLinks

            if (this.delelinks.length !== 0) {
              const _link = this.delelinks.filter(_link => _link.to === nodeId)

              if (_link.length !== 0) {
                for (const prop in _link) {
                  createLink(_link[prop])
                  this.delelinks.splice(this.delelinks.indexOf(_link[prop]), 1)
                }
              }
              this.addLoopNode(nodeId)
            }

            updateNodesWithText()
            updateLinksWithText()
            clearFocus()
            this.svgElements.root.select('.menuCircle').remove()
            layoutMode === 'GRID' && restoreLayout(true)
          }
        })
      }
    },

    returnGroups() {
      return this.selectedGroups
    },

    addLoopNode(nodeId) {
      const {
        nodes,
        links,
        createNode,
        createLink,
      } = this
      const _linkto = this.delelinks.filter(_linkto => _linkto.from === nodeId)


      console.log("1230展开测试：" + JSON.stringify(_linkto))

      if (_linkto.length !== 0) {
        for (const prop in _linkto) {
          // delete nodes
          const nodeto = this.delenodes.filter(nodeto => nodeto.id === _linkto[prop].to)[0]
          if (typeof (nodeto) !== 'undefined') {
            // add nodes
            createNode(nodeto)
            this.delenodes.splice(this.delenodes.indexOf(nodeto), 1)
            // add links
            createLink(_linkto[prop])
            this.delelinks.splice(this.delelinks.indexOf(_linkto[prop]), 1)
            this.addLoopNode(_linkto[prop].to)
          }
        }
      }
    },
    //收起
    deleteMyLinks(nodeOrId) {
      // console.log('[delBoard]', nodeOrId)
      const {
        nodes,
        links,
        updateNodesWithText,
        updateLinksWithText,
        clearFocus,
        layoutMode,
        restoreLayout
      } = this
      // console.log('[delBoard]', nodes)
      const nodeId = nodeOrId.id
      const nodegroup = nodeOrId.group
      //收起的Links
      const node = nodes.filter(node => node.id === nodeId)[0]
      const _link = links.filter(_link => _link.from === nodeId)

      for (const prop in _link) {
        // delete links
        // links.splice(links.indexOf(_link[prop]), 1)
        this.delelinks.push(_link[prop])
      }

      this.deleLoopNode(nodeId)
      //console.log('[delelinks]',this.delelinks)
      updateNodesWithText()
      updateLinksWithText()
      clearFocus()
      this.svgElements.root.select('.menuCircle').remove()
      layoutMode === 'GRID' && restoreLayout(true)
    },
    deleLoopNode(nodeId) {
      const {
        nodes,
        links,
      } = this

      console.log("收起测试" + JSON.stringify(this.delelinks))

      const _linkto = links.filter(_linkto => _linkto.from === nodeId)

      if (_linkto.length !== 0) {
        for (const prop in _linkto) {
          // delete links
          links.splice(links.indexOf(_linkto[prop]), 1)
          this.delelinks.push(_linkto[prop])
          // delete nodes
          const nodeto = nodes.filter(nodeto => nodeto.id === _linkto[prop].to)[0]

          if (typeof (nodeto) !== 'undefined') {
            nodes.splice(nodes.indexOf(nodeto), 1)
            this.delenodes.push(nodeto)
            this.deleLoopNode(_linkto[prop].to)
          }
        }
      }
    },
    // 点击节点
    clickNode(e) {
      console.log("输出鼠标按键编号:" + e.which);

      // console.log("1298点击测试" + JSON.stringify(e))
      const {
        config: {baseRadius},
        nodes,
        flags: {enableFocus},
        svgElements: {root, focusGroup: fg},
        getNodeById,
      } = this
      this.clearFocus()
      const id = Number(e.target.attributes['data-id'].value)

      const node = getNodeById(id)

      console.log("点击了节点561651：" + JSON.stringify(node))
      node.textSize = parseInt(node.textSize)
      // console.log("点击测试85556:"+JSON.stringify(node))
      // node.group = JSON.parse(node.group)
      // console.log("点击测试85557："+JSON.stringify(node))
      if (node.isDelete)
        return

      //const originFocus = fg.selectAll('circle')
      //console.log(`click node: id=${id}, `, root)


      if (this.flags.selectingMode) {
        this.flags.selectingMode = false
        console.log("数据测试111：执行了节点点击方法")
        this.$emit('editor-action', 'catchNode', node,)
      } else {
        this.setNodeFocus(node)

        // //wx
        // this.toggleCircle_wx(this.svgElements.svg, e)

        this.$emit('editor-action', 'selectNode', {...node})
        this.$emit('msg-action', 'setSituationid', {...node})
        // console.log(`click node t2: id=${node.id}, `, node)
        //调用收起、展开功能
        // this.toggleCircle(root, node)

        //圆环菜单构建方法
        // this.toggleCircle_wx(root, node)

      }
    },
    // 点击关系
    clickLink(e) {
      if (this.flags.selectingMode) return
      const id = Number(e.target.attributes['data-id'].value)
      const link = this.getLinkById(id)
      console.log(`click link: id=${id}, `, link)

      this.clearFocus()
      this.$emit('editor-action', 'selectLink', {...link})
    },
    clickView() {
      if (this.flags.selectingMode) return
      this.clearFocus()
      this.$emit('editor-action', 'selectNone')
    },

    // 聚焦(高亮显示)
    focus(e) {
      const {
        getNodeById,
        setFocus,
        flags: {enableFocus},
        root,
      } = this
      if (!enableFocus) return
      const id = Number(e.target.attributes['data-id'].value)
      const targetNode = getNodeById(id)

      if (targetNode.haveCircle)
        this.toggleCircle_wx(root, targetNode)

      //console.log(`focus: id=${id}, `, targetNode)
      if (!targetNode || targetNode.isDelete) return

      setFocus(targetNode)
    },
    // 取消聚焦
    unfocus(e) {

      // console.log("焦点移除" + JSON.stringify(e))
      const {
        flags: {enableFocus},
        _unfocus
      } = this
      if (!enableFocus) return

      const id = Number(e.target.attributes['data-id'].value)
      _unfocus(id)
    },
    _unfocus(id) {
      const {
        svgElements: {focusGroup: fg},
        getNodeById,
        setNodeHighLight
      } = this
      const targetNode = getNodeById(id)
      setNodeHighLight(targetNode, false)
      const focusNode = fg.select(`#focus-node-${id}`)
      if (!focusNode.empty()) {
        if (targetNode && targetNode.focus) return

        focusNode.remove()

      }
    },
    /********** 力导图绑定事件 **********/
    // 拖曳设置
    setDrag(simulation) {

      const {setEnableFocus, flags, nodes} = this

      const start = (e, d) => {
        if (!e.active) simulation.alphaTarget(0.3).restart()
        setEnableFocus(false)
        if (flags.locked) {
          const group = d.group
          nodes
            .filter(node => node.group === group)
            .forEach(node => {
              node.fx = d.x
            })

        } else {
          d.fx = d.x
          d.fy = d.y
        }
      }

      const drag = (e, d) => {
        if (flags.locked) {
          const group = d.group
          nodes
            .filter(node => node.group === group)
            .forEach(node => {
              node.fx = e.x
            })
        } else {
          d.fx = e.x
          d.fy = e.y
        }
      }

      const end = (e, d) => {
        setEnableFocus(true)
        if (flags.locked) return
        if (!e.active) simulation.alphaTarget(0)
        d.x = d.fx
        d.y = d.fy
        if (!flags.pinned) {
          d.fx = null
          d.fy = null
        }
      }

      const boundDrag = this.$d3
        .drag()
        .on('start', start)
        .on('drag', drag)
        .on('end', end)
      this.svgElements.boundDrag = boundDrag

      return boundDrag
    },
    // 缩放&平移设置
    setZoom(root) {
      const boundZoom = this.$d3.zoom().on('zoom', e => {
        root.attr('transform', e.transform)
      })
      this.svgElements.boundZoom = boundZoom
      return boundZoom
    },
    /********** 数据属性操作 **********/
    // 根据 id 查找节点
    getNodeById(id) {
      return this.nodes.filter(node => node.id === id)[0]
    },
    getNode(nodeOrId) {
      return typeof nodeOrId === 'object'
        ? nodeOrId
        : this.getNodeById(nodeOrId)
    },
    // 根据 id 查找关系
    getLinkById(id) {
      console.log("getLinkById",this.links.filter(link => link.id === id)[0])
      return this.links.filter(link => link.id === id)[0]
    },
    // 设置选中组别
    setSelectedGroups(groups) {
      // console.log("组别测试sadas" + JSON.stringify(this.selectedGroups))
      this.selectedGroups = groups
      // console.log("组别测试sadas" + JSON.stringify(this.selectedGroups))
      this.updateAllOpacity()
    },
    // updateAllOpacity() {
    //   const {selectedGroups: groups, nodes, links} = this
    //   nodes.forEach(node => {
    //     // node.color = node.color ? "#" + node.color.slice(1, node.color.length) + "33" : this.svgElements.scale(node.group)+"33"
    //   })
    //   links.forEach(link => {
    //     link.opacity = link.source.opacity || link.target.opacity
    //   })
    // },

    updateAllOpacity() {
      const {selectedGroups: groups, nodes, links} = this
      if (nodes) {
        nodes.forEach(node => {
          node.opacity = !groups.includes(node.group)
          // console.log(node.opacity)
        })
        links.forEach(link => {
          link.opacity = link.source.opacity || link.target.opacity
          // console.log(link.opacity)
        })
      }
    },

    // 设置节点高亮标志
    setNodeHighLight(nodeOrId, bool = false) {
      const node = this.getNode(nodeOrId)
      node.highLight = bool
    },
    setNodeFocus(nodeOrId) {
      const {
        nodes,
        flags: {singleFocus},
        _unfocus,
        getNode
      } = this
      const node = getNode(nodeOrId)

      if (singleFocus) {
        nodes
          .filter(n => n !== node)
          .forEach(node => {
            if (node.focus) {
              node.focus = false
              _unfocus(node.id)
            }
          })
      }
      node.focus = true
    },
    clearNodeFocus(nodeOrId) {
      const node = this.getNode(nodeOrId)
      node.focus = false
    },
    getNodesByIds(ids) {
      return this.nodes.filter(node => ids.includes(node.id))
    },
    /********** 图谱标志 **********/
    setEnableFocus(bool = true) {
      this.flags.enableFocus = bool
    },
    setLocked(bool = false) {
      this.flags.locked = bool
    },
    /********** 图谱标志 **********/
    exportPng() {
      const projectName = this.projectInfo.name
      const svg = this.svgElements.svg
      this.saveAsPng({projectName, svg})

    },
    exportXml() {
      const {projectId, name} = this.projectInfo
      console.log("信息获取789432：" + projectId + "  " + name)
      this.saveAsXml({projectId, name})
    },

    //wx
    toggleCircle_wx(current, d) {
      // console.log("执行了右击方法")
      const {
        config: {baseRadius},
        $d3,
        addMyLinks,
        deleteMyLinks,
        fakeDelete,
        copyNodeInGraph
      } = this
      d.haveCircle = true
      this.nodes.forEach(node => {
        if (!this.allNodes.includes(node.id))
          this.allNodes.push(node.id)
      })

      var currentD = d
      if (d.clickFlag) {
        // removeSingle()
        // document.getElementById('xxx').innerText = ''
      }
      d.clickFlag = true
      // document.getElementById('xxx').innerText = d.name
      var data = [
        {
          population: 30,
          value: '复制',
          type: 'copy'
        }, {
          population: 30,
          value: '收起',
          type: 'showOn'
        }, {
          population: 30,
          value: '展开',
          type: 'showOff'
        }]
      var sum = $d3.sum(data.map(function (d) {
        return d.population
      }))
      for (var i in data) {
        data[i].Percentage = (data[i].population / sum * 100).toFixed(0) + "%";
      }

      //wx
      var width = baseRadius*2.2 + currentD.radius * 45,
        height = baseRadius*2.2 + currentD.radius * 45;

      // margin = { "left": 30, "top": 30, "right": 30, "bottom": 30 },
      //   svg_width = width + margin.left + margin.right,
      //   svg_height = height + margin.top + margin.bottom,
      //   font_size = 15;
      // svg_width = width + margin.left + margin.right,
      // svg_height = height + margin.top + margin.bottom,

      var g = current
        .append("g")
        .attr('class', 'menuCircle')
        .attr("width", width)
        .attr("height", height)

      var Pie = g.append("g")
        .attr("transform", function (d) {
          var x1 = currentD.x
          var y1 = currentD.y
          return 'translate(' + x1 + ',' + y1 + ')';
        })

      var arc_generator = $d3.arc()
        .innerRadius(width / 4)
        .outerRadius(width / 3)
      var angle_data = $d3.pie()
        .value(function (d) {
          return d.population;
        })
      var pieData = angle_data(data)
      var pieAngle = pieData.map(function (p) {
        return (p.startAngle + p.endAngle) / 2 / Math.PI * 180;
      });

      //生成圆环背景
      Pie.selectAll("path")
        .data(angle_data(data))
        .enter()
        .append("path")
        .attr("d", arc_generator)
        .style("fill", function (d, i) {
          return 'AliceBlue';
        })
        .style("opacity", 1)
        .style('stroke', 'black')
        .attr("class", "path")
        .attr('type', function (d) {
          return d.data.type
        })
        .on('click', function (d) {
          if (d.target.attributes['type'].value === 'showOn') {
            deleteMyLinks(currentD)
          } else if (d.target.attributes['type'].value === 'copy') {
            // deleteItemNode(currentD)
            //假删除,将节点变为透明状态
            // fakeDelete(currentD)
            copyNodeInGraph(currentD)
          } else {
            addMyLinks(currentD)
          }
          // this.$d3.event.stopPropagation()
        })


      var arc_label = $d3.arc()
        .innerRadius(width / 4)
        .outerRadius(width / 3)

      Pie.selectAll(".arc_label")
        .data(angle_data(data))
        .enter()
        .append("path")
        .attr("d", arc_label)
        .attr("class", "arc_label")
        .style("fill", "none")
      const labelFontSize = currentD.radius * 3;
      const labelValRadius = width / 4 + labelFontSize*0.3; // 计算正确半径 文字位置
      const labelValRadius1 = width / 3;
      // const labelFontSize = currentD.radius * 2;
      // const labelValRadius = (currentD.radius * 45 * 0.3 - labelFontSize * 0.3); // 计算正确半径 文字位置
      // const labelValRadius1 = (currentD.radius * 45 * 0.3 + labelFontSize * 0.3);

      // const labelFontSize = 10;
      // const labelValRadius = (currentD.radius * 50 * 0.35 - labelFontSize * 0.35); // 计算正确半径 文字位置
      // const labelValRadius1 = (currentD.radius * 50* 0.35 + labelFontSize * 0.35);


      const labelsVals = current.select('.menuCircle').append('g')
        .attr("transform", function (d) {
          var x1 = currentD.x
          var y1 = currentD.y
          return 'translate(' + x1 + ',' + y1 + ')';
        })
        .classed('labelsvals', true);

      // 定义两条路径以使标签的方向正确
      labelsVals.append('def')
        .append('path')
        .attr('id', 'label-path-1')
        .attr('d', `m0 ${-labelValRadius} a${labelValRadius} ${labelValRadius} 0 1,1 -0.01 0`)

      labelsVals.append('def')
        .append('path')
        .attr('id', 'label-path-2')
        .attr('d', `m0 ${-labelValRadius1} a${labelValRadius1} ${labelValRadius1} 0 1,0 0.01 0`)

      //生成圆环文字
      labelsVals.selectAll('text')
        .data(data)
        .enter()
        .append('text')
        .style('font-size', labelFontSize)
        .style('fill', 'black')
        .style('font-weight', "bold")
        .style('text-anchor', 'middle')
        .append('textPath')
        .attr('href', function (d, i) {
          const p = pieData[i];
          const angle = pieAngle[i];
          if (angle > 90 && angle <= 270) { // 根据角度选择路径
            return '#label-path-2';
          } else {
            return '#label-path-1';
          }
        })
        .attr('startOffset', function (d, i) {
          const p = pieData[i];
          const angle = pieAngle[i];
          let percent = (p.startAngle + p.endAngle) / 2 / 2 / Math.PI * 100;
          if (angle > 90 && angle <= 270) { // 分别计算每条路径的正确百分比
            return 100 - percent + "%";
          }
          return percent + "%";
        })
        .text(function (d) {
          return d.value;
        })

        .on('click', function (d) {
          if (d.target.__data__.type === 'showOn') {
            deleteMyLinks(currentD)
          } else if (d.target.__data__.type === 'copy') {
            // deleteItemNode(currentD)
            // fakeDelete(currentD)
            copyNodeInGraph(currentD)
          } else {
            addMyLinks(currentD)
          }
          // $d3.event.stopPropagation()
        }, true)

    },

    //wx环形菜单删除节点
    deleteItemNode(currentD) {
      const {
        //   updateNodesWithText,
        // updateLinksWithText,
        // clearFocus,
        //   layoutMode,
        setEnableFocus,
        reset,
        restoreLayout
      } = this

      const projectId = this.projectInfo.projectId
      const id = currentD.id

      console.log("环形菜单删除：" + projectId + " " + id)

      this.deleteItemNodeAct({projectId, id}).then(res => {
        if (res) {
          const action = 'deleteNode'
          // node === 'node' ? 'deleteNode' : node === 'link' ? 'deleteLink' : ''
          if (action) {
            this.$emit('graph-action', action, id)
            this.$emit('graph-action', 'clearFocus')
            this.init()
          }
        }
      })

      // updateNodesWithText()
      // updateLinksWithText()
      // clearFocus()
      // this.svgElements.root.select('.menuCircle').remove()
      // layoutMode === 'GRID' && restoreLayout(true)

      // this.deleteItemAct({ projectid, node, id }).then(res => {
      //   if (res) {
      //     const action =
      //       type === 'node' ? 'deleteNode' : type === 'link' ? 'deleteLink' : ''
      //     if (action) {
      //       this.$emit('graph-action', action, id)
      //       this.selectNone()
      //     }
      //   }
      // })

    },

    //wx2.9
    fakeDelete(nodeOrId) {
      const {
        $d3,
        links,
        updateNodesWithText,
        updateLinksWithText,
        clearFocus,
        layoutMode,
        changeColorDim,
        restoreLayout,
        updateNodesWithTextColor,
        // svgElements:{
        //   svgLinks,
        //   svgNodesText
        // }

      } = this
      const nodeId = nodeOrId.id

      this.colorJudge = []
      this.colorJudge.push(nodeOrId.id)

      // const node = nodes.filter(node => node.id === nodeId)[0]
      changeColorDim(nodeId)
      links.filter(link => {
        if (link.source.id === nodeId || this.colorJudge.includes(link.source.id)) {
          changeColorDim(link.target.id)
          this.colorJudge.push(link.target.id)
        }
      })

      console.log("删除节点编号" + this.colorJudge)

      updateNodesWithText()
      updateLinksWithText()
      clearFocus()
      this.svgElements.root.select('.menuCircle').remove()
      layoutMode === 'GRID' && restoreLayout(true)
      // console.log("1789link: " + JSON.stringify(_link))

    },

    //假删除颜色变淡
    changeColorDim(nodeId) {
      const {
        nodes,
      } = this
      nodes.filter(node => {
        if (node.id === nodeId) {
          if (node.color.length == 9 && +" " + node.color.slice(7, node.color.length)) {
          } else {
            node.color = node.color ? "#" + node.color.slice(1, node.color.length) + "33" : this.svgElements.scale(node.group) + "33"
            node.isDelete = true
            console.log("1830测试wx：" + node.isDelete)
          }
        }
      })
      // this.updateAllOpacity()
    },

    doubleClick(e) {
      const {
        getNodeById,
        addMyLinks
      } = this

      this.clearFocus()
      const id = Number(e.target.attributes['data-id'].value)
      const node = getNodeById(id)
      if (!node.isDelete) {
        addMyLinks(node)
      }
      console.log("1843执行了双击" + node.id)
    },

    //wx2.13
    setCirileMenu(root) {
      const menuGroup = root
        .append('g')
        .attr('class', 'circle-menu')
        .attr("")
      this.svgElements.menuGroup = menuGroup
    },

    searchAddOne(data) {
      const {
        updateNodesWithText,
        updateLinksWithText,
        clearFocus,
        layoutMode,
        restoreLayout,
        nodes,
        createLink
      } = this
      console.log(data)

      this.showMore(data).then(res => {
        if (res) {
          // console.log("检索新增1：" + JSON.stringify(res))
          res.nodes.forEach(node => {
            if (!this.allNodes.includes(node.id)) {
              // res.links.forEach(link => {
              //   if (link.target === node.id) {
              //     this.middleLinks.push(link)
              //   }
              // })
              node.isDelete = false
              node.opacity = false
              node.isNew = true
              node.haveCircle = false

              nodes.push(node)
              // console.log("1254测试：" + JSON.stringify(nodes))
              // if (!this.selectedGroups.includes(node.group)) {
              //   this.selectedGroups.push(node.group)
              // }
              this.allNodes.push(node.id)
            }
          })

          updateNodesWithText()
          // updateLinksWithText()
          clearFocus()
          this.svgElements.root.select('.menuCircle').remove()
          layoutMode === 'GRID' && restoreLayout(true)
        }
      })
    },

    //右击节点方法
    rightClickNode(e) {
      const {
        getNodeById,
        svgElements: {root, focusGroup: fg},
      } = this
      this.clearFocus()
      const id = Number(e.target.attributes['data-id'].value)
      const node = getNodeById(id)
      // console.log("执行了右击方法"+JSON.stringify(node))
      if (!this.flags.selectingMode) {
        //圆环菜单构建方法
        this.toggleCircle_wx(root, node)
      }
    },
    copyNodeInGraph(node) {
      const {
        projectInfo,
        updateNodesWithText
      } = this
      // console.log("复制节点22323:"+JSON.stringify(node))
      const item = {
        id: node.id,
        name: node.name,
        group: node.group,
        radius: node.radius,
        color: node.color,
        figure: node.figure,
        textSize: node.textSize,
        properties: node.properties
      }
      console.log("重构了item:" + JSON.stringify(item))

      const props = []
      for (const name in item.properties) {
        props.push({name, value: item.properties[name]})
      }
      item.properties = props

      const copySign = true
      const projectId = projectInfo.projectId
      const type = 'node'
      console.log("数据测试222:" + JSON.stringify(projectId))
      console.log("数据测试222:" + JSON.stringify(type))
      console.log("数据测试222:" + JSON.stringify(item))
      console.log("数据测试222:" + JSON.stringify(copySign))
      this.createItemAct({projectId, type, item, copySign}).then(item => {
        console.log("复制了节点：" + JSON.stringify(item))
        if (item) {
          if (type === 'node') {
            this.createNode(item)
            // this.$emit('graph-action', 'createNode', item)
            this.selectNode(item)
          }
          // else if (type === 'link') {
          //   this.$emit('graph-action', 'createLink', item)
          //   this.selectLink(item)
          // }
        }
      })
      // this.createNode()
    },

    isContains(returnNode) {
      if (this.nodes.contain(returnNode))
        return true;
      else
        return false;
    },


    /********** 时间轴 **********/
    createTimeline() {

    }
  }
}
</script>

<style scoped>
#graph {
  width: 100%;
  height: 100%;
  overflow: hidden;
}
</style>
